package mysql

import (
	"errors"
	"fmt"
	"gitee.com/captials-team/ubdframe/src/domain/dto/paginate"
	"gitee.com/captials-team/ubdframe/src/domain/interfaces"
	"gorm.io/gorm"
	"reflect"
	"strings"
)

type GormDaoHelp struct {
}

func (d GormDaoHelp) BeInitSubTable(db *gorm.DB, table string, entity interface{}) error {
	if db.Migrator().HasTable(table) {
		return nil
	}
	err := db.Table(table).Migrator().CreateTable(entity)

	//并发下出现表已存在的err则忽略处理
	if err != nil && strings.IndexAny(err.Error(), "already exists") >= 0 {
		return nil
	}
	return err
}

func (d GormDaoHelp) FetchTables(db *gorm.DB, tableLike string) ([]string, error) {
	var tables []string

	sql := "SHOW TABLES"
	if len(tableLike) > 0 {
		sql += fmt.Sprintf(" LIKE '%s'", tableLike)
	}
	ret := db.Raw(sql).Scan(&tables)
	if ret.Error != nil {
		return tables, ret.Error
	}

	return tables, nil
}

func (d GormDaoHelp) OptimizeTable(db *gorm.DB, table string) (int64, error) {
	//手动释放磁盘空间
	sql := fmt.Sprintf("OPTIMIZE TABLE %s", table)
	ret := db.Exec(sql)

	return ret.RowsAffected, ret.Error
}

func (d GormDaoHelp) ExecSql(db *gorm.DB, sql ...string) error {
	if len(sql) == 0 {
		return nil
	}
	ret := db.Exec(strings.Join(sql, ";"))
	if ret.Error != nil {
		return ret.Error
	}
	return nil
}

func NewGormDBFacade[T interface{}](db *gorm.DB) *GormDBFacade[T] {
	return &GormDBFacade[T]{
		db:    db,
		model: new(T),
	}
}

type GormDBFacade[T interface{}] struct {
	model *T
	db    *gorm.DB
}

// All 查询全部
func (fa *GormDBFacade[T]) All(scopes ...interfaces.GormApplyQuery) ([]*T, error) {
	db := fa.db.Model(fa.model)
	db = fa.applyScopes(db, scopes...)

	var list []*T

	if ret := db.Find(&list); ret.Error != nil {
		return list, ret.Error
	}
	return list, nil
}

// Search 分页搜索
func (fa *GormDBFacade[T]) Search(pa *paginate.Pager, scopes ...interfaces.GormApplyQuery) ([]*T, *paginate.Pager, error) {
	if pa == nil {
		pa = &paginate.Pager{}
	}
	pa.Correct()

	db := fa.db.Model(fa.model)
	db = fa.applyScopes(db, scopes...)

	var list []*T
	if ret := db.Count(&pa.Total); ret.Error != nil {
		return list, pa, ret.Error
	}

	db = db.Limit(pa.Size).Offset(pa.Offset())

	if ret := db.Find(&list); ret.Error != nil {
		return list, pa, ret.Error
	}
	pa.Correct()
	return list, pa, nil
}

// QueryByScope 查询单条记录
func (fa *GormDBFacade[T]) QueryByScope(scopes ...interfaces.GormApplyQuery) (*T, error) {
	var info T
	db := fa.db.Model(fa.model)
	db = fa.applyScopes(db, scopes...)
	ret := db.First(&info)
	if errors.Is(ret.Error, gorm.ErrRecordNotFound) {
		return nil, nil
	}
	if ret.Error != nil {
		return nil, ret.Error
	}
	return &info, nil
}

// Query 查询单条记录
func (fa *GormDBFacade[T]) Query(id int64) (*T, error) {
	var info T
	db := fa.db.Model(fa.model)
	ret := db.Where("id", id).First(&info)
	if errors.Is(ret.Error, gorm.ErrRecordNotFound) {
		return nil, nil
	}
	if ret.Error != nil {
		return nil, ret.Error
	}
	return &info, nil
}

func (fa *GormDBFacade[T]) applyScopes(db *gorm.DB, scopes ...interfaces.GormApplyQuery) *gorm.DB {
	if len(scopes) > 0 {
		for _, a := range scopes {
			db = a(db)
		}
	}
	return db
}

func (fa *GormDBFacade[T]) Add(add T) (int64, error) {
	db := fa.db.Model(fa.model)
	ret := db.Model(fa.model).Create(&add)
	if ret.Error != nil {
		return 0, ret.Error
	}
	if ret.RowsAffected == 0 {
		return 0, fmt.Errorf("add fail")
	}

	r := reflect.ValueOf(add)
	return r.FieldByName("Id").Int(), nil
}

func (fa *GormDBFacade[T]) Update(id int64, up T) (int64, error) {
	if id <= 0 {
		return 0, fmt.Errorf("not find one")
	}
	db := fa.db.Model(fa.model)
	ret := db.Where("id", id).Updates(&up)
	if ret.Error != nil {
		return 0, ret.Error
	}
	if ret.RowsAffected == 0 {
		return 0, fmt.Errorf("no update %d", id)
	}
	return ret.RowsAffected, nil
}

func (fa *GormDBFacade[T]) Delete(id int64) (int64, error) {
	db := fa.db.Model(fa.model)
	ret := db.Where("id=?", id).Where("id>0").Delete(new(T))
	return ret.RowsAffected, ret.Error
}

func (fa *GormDBFacade[T]) DeleteByScope(scopes ...interfaces.GormApplyQuery) (int64, error) {
	db := fa.db.Model(fa.model)
	db = fa.applyScopes(db, scopes...)
	ret := db.Delete(new(T))
	return ret.RowsAffected, ret.Error
}

func (fa *GormDBFacade[T]) ResetStatus(id int64, status int) (int64, error) {
	db := fa.db.Model(fa.model)
	ret := db.Model(new(T)).Where("id=?", id).Update("status", status)
	if ret.Error != nil {
		return 0, ret.Error
	}
	return ret.RowsAffected, nil
}

func (fa *GormDBFacade[T]) UseDB(db *gorm.DB) interfaces.DBFacade[T] {
	return &GormDBFacade[T]{
		model: fa.model,
		db:    db,
	}
}

func (fa *GormDBFacade[T]) New(db *gorm.DB) *GormDBFacade[T] {
	return &GormDBFacade[T]{
		model: fa.model,
		db:    db,
	}
}

func (fa *GormDBFacade[T]) Migrate() (int64, error) {
	return 1, fa.db.AutoMigrate(new(T))
}

func (fa *GormDBFacade[T]) DB() *gorm.DB {
	return fa.db
}
